package com.zeyu.framework.monitors.server;

import com.beust.jcommander.internal.Lists;
import com.zeyu.framework.monitors.Collector;
import com.zeyu.framework.monitors.server.entity.CPUStatus;
import com.zeyu.framework.monitors.server.entity.DiskStatus;
import com.zeyu.framework.monitors.server.entity.NetInfoStatus;
import com.zeyu.framework.monitors.server.entity.ProcStatus;
import com.zeyu.framework.monitors.server.entity.ServerStatus;
import com.zeyu.framework.monitors.server.service.ServerCPUService;
import com.zeyu.framework.monitors.server.service.ServerService;
import com.zeyu.framework.monitors.server.utils.SigarUtils;
import com.zeyu.framework.utils.IPUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.hyperic.sigar.CpuInfo;
import org.hyperic.sigar.CpuPerc;
import org.hyperic.sigar.FileSystem;
import org.hyperic.sigar.FileSystemUsage;
import org.hyperic.sigar.NetInfo;
import org.hyperic.sigar.NetInterfaceConfig;
import org.hyperic.sigar.NetInterfaceStat;
import org.hyperic.sigar.NfsFileSystem;
import org.hyperic.sigar.OperatingSystem;
import org.hyperic.sigar.ProcCredName;
import org.hyperic.sigar.ProcMem;
import org.hyperic.sigar.ProcState;
import org.hyperic.sigar.ProcTime;
import org.hyperic.sigar.ProcUtil;
import org.hyperic.sigar.Sigar;
import org.hyperic.sigar.SigarException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.net.InetAddress;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;

/**
 * 执行Server的采集任务，将来可能会被任务调度使用，都是独立的方法
 * Created by zeyuphoenix on 16/8/26.
 */
@Component
public class ServerCollector implements Collector {

    // ================================================================
    // Constants
    // ================================================================

    /**
     * logger
     */
    private static final Logger logger = LoggerFactory.getLogger(ServerCollector.class);

    // ================================================================
    // Fields
    // ================================================================

    @Autowired
    private ServerService serverService;

    @Autowired
    private ServerCPUService serverCPUService;

    // 保存最后一次取得的状态
    private ServerStatus serverStatus;

    static {
        SigarUtils.loadLibrary();
    }

    // ================================================================
    // Constructors
    // ================================================================

    // ================================================================
    // Methods from/for super Interfaces or SuperClass
    // ================================================================

    @Override
    public void collect() {
        logger.debug("Server收集任务启动...");
        // 防止这是第一个启动模块，数据库连接未初始化
        serverStatus = getCurrentServerStatus();

        // 初始化时间
        serverStatus.setTime(new Date());

        // 保存信息到数据库
        serverService.save(this.serverStatus);
        for (CPUStatus cpuStatus : this.serverStatus.getCpuStatusList()) {
            serverCPUService.save(cpuStatus);
        }
    }

    // ================================================================
    // Public or Protected Methods
    // ================================================================

    public ServerStatus getServerStatus() {
        return this.serverStatus;
    }

    /**
     * 获取当前Server的状态
     */
    public ServerStatus getCurrentServerStatus() {
        ServerStatus serverStatus = new ServerStatus();

        Sigar sigar = new Sigar();
        try {
            serverStatus.setStatus(true);

            try {
                NetInfo info = sigar.getNetInfo();
                // IP，名称
                serverStatus.setIp(InetAddress.getLocalHost().getHostAddress());
                // 网关地址
                serverStatus.setGateway(info.getDefaultGateway());
                // 名称.
                serverStatus.setHostName(info.getHostName());
                // 网卡信息.
                serverStatus.setDomainName(info.getDomainName());
                // dns
                serverStatus.setPrimaryDns(info.getPrimaryDns());
                serverStatus.setSecondaryDns(info.getSecondaryDns());
            } catch (Exception e) {
                logger.error("server monitor ip,name error!");
            }
            // 在线时长
            try {
                long uptime = (long) sigar.getUptime().getUptime();
                serverStatus.setUptime(uptime);
            } catch (Exception e) {
                logger.error("server monitor uptime error!");
            }

            // 内存信息
            try {
                long kb = 1024L;

                // 最大可使用内存 MB RAM
                long maxMemory = sigar.getMem().getRam();
                serverStatus.setMaxMemory(maxMemory);

                // 总的物理内存 Memory
                long totalMemory = sigar.getMem().getTotal() / kb;
                serverStatus.setTotalMemory(totalMemory);
                // 剩余的物理内存
                long freeMemory = sigar.getMem().getFree() / kb;
                serverStatus.setFreeMemory(freeMemory);
                // 已使用的物理内存
                long usedMemory = sigar.getMem().getUsed() / kb;
                serverStatus.setUsedMemory(usedMemory);

                // 内存使用率 DB
                double usedPercent = (double) sigar.getMem().getUsed()
                        / (double) sigar.getMem().getTotal();
                serverStatus.setUsedMemoryPercent(usedPercent);

                // 剩余内存 -/+ buffers/cache:
                long actualFreeMemory = sigar.getMem().getActualFree() / kb;
                serverStatus.setActualFreeMemory(actualFreeMemory);
                // 使用内存
                long actualUsedMemory = sigar.getMem().getActualUsed() / kb;
                serverStatus.setActualUsedMemory(actualUsedMemory);

                // 交换区总共 Swap
                long swapTotalMemory = sigar.getSwap().getTotal() / kb;
                serverStatus.setSwapTotalMemory(swapTotalMemory);
                // 交换区使用
                long swapUsedMemory = sigar.getSwap().getUsed() / kb;
                serverStatus.setSwapUsedMemory(swapUsedMemory);
                // 交换区剩余
                long swapFreeMemory = sigar.getSwap().getFree() / kb;
                serverStatus.setSwapFreeMemory(swapFreeMemory);

            } catch (Exception e) {
                logger.error("server monitor Memory error!");
            }
            // 操作系统
            try {
                OperatingSystem sys = OperatingSystem.getInstance();

                // OS name
                String osName = sys.getName();
                if (osName == null) {
                    osName = System.getProperty("os.name");
                }
                serverStatus.setOsName(osName);

                // OS description
                serverStatus.setDescription(sys.getDescription());
                // OS arch
                serverStatus.setArch(sys.getArch());
                // OS version
                serverStatus.setVersion(sys.getVersion());
                // OS vendor
                serverStatus.setVendor(sys.getVendor());
                // OS vendor version
                serverStatus.setVendorVersion(sys.getVendorVersion());

            } catch (Exception e) {
                logger.error("server monitor os.name error!");
            }
            // CPU信息
            try {
                // CPU数量（单位：个）
                CpuInfo infos[] = sigar.getCpuInfoList();
                serverStatus.setCpuNum(infos.length);
                CpuPerc cpuList[] = sigar.getCpuPercList();
                // 设置cpu信息

                for (int i = 0; i < infos.length; i++) {
                    CPUStatus cpuStatus = new CPUStatus();

                    // 变化信息
                    CpuPerc perc = cpuList[i];
                    // CPU总使用率 DB
                    float cpuRatio = (float) (1 - perc.getIdle());
                    cpuStatus.setCpuRatio(cpuRatio);
                    // CPU系统使用率 DB
                    float cpuSysRatio = (float) perc.getSys();
                    cpuStatus.setCpuSysRatio(cpuSysRatio);
                    // CPU用户使用率 DB
                    float cpuUsrRatio = (float) perc.getUser();
                    cpuStatus.setCpuUsrRatio(cpuUsrRatio);

                    cpuStatus.setInd(i);
                    cpuStatus.setTime(new Date());

                    // 不变信息
                    CpuInfo info = infos[i];
                    // 缓冲存储器数量
                    cpuStatus.setCacheSize(info.getCacheSize() + "");
                    // 厂家
                    cpuStatus.setVendor(info.getVendor());
                    // 模式
                    cpuStatus.setModel(info.getModel());
                    // 主频
                    cpuStatus.setMhz(info.getMhz() + "Mhz");
                    cpuStatus.setServerStatus(serverStatus);

                    serverStatus.getCpuStatusList().add(cpuStatus);
                }

            } catch (Exception e) {
                logger.error("server monitor CpuInfo error!");
            }
            // JVM信息
            try {
                // Java vm vendor
                serverStatus
                        .setJavaVendor(System.getProperty("java.vm.vendor"));
                // Java home
                serverStatus.setJavaHome(System.getProperty("java.home"));
                // Java version
                serverStatus.setJavaVersion(System.getProperty("java.version"));

                // memory info.
                Runtime runtime = Runtime.getRuntime();
                serverStatus.setJvmMaxMemory(runtime.maxMemory());
                serverStatus.setJvmTotalMemory(runtime.totalMemory());
                serverStatus.setJvmFreeMemory(runtime.freeMemory());

                serverStatus.setTime(new Date());

            } catch (Exception e) {
                logger.error("server monitor jvmInfo error!");
            }
        } finally {
            // 需要关闭会话，不然长时间创建会话，会导致jvm宕机退出
            sigar.close();
        }

        return serverStatus;
    }

    /**
     * 获取服务器的磁盘状态
     */
    public List<DiskStatus> getDiskStatusList() {

        List<DiskStatus> diskStatusList = Lists.newArrayList();
        Sigar sigar = new Sigar();
        try {
            // 获取盘符和详细信息
            FileSystem[] fileSystemList = sigar.getFileSystemList();
            if (ArrayUtils.isNotEmpty(fileSystemList)) {
                for (FileSystem fileSystem : fileSystemList) {
                    DiskStatus diskStatus = new DiskStatus();
                    diskStatusList.add(diskStatus);

                    // 使用状态
                    long used, avail, total, pct, diskReadBytes, diskWriteBytes;
                    try {
                        FileSystemUsage usage;
                        if (fileSystem instanceof NfsFileSystem) {
                            NfsFileSystem nfs = (NfsFileSystem) fileSystem;
                            if (!nfs.ping()) {
                                logger.error(nfs.getUnreachableMessage());
                                continue;
                            }
                        }
                        usage = sigar.getFileSystemUsage(fileSystem
                                .getDirName());
                        // KB
                        used = usage.getTotal() - usage.getFree();
                        avail = usage.getAvail();
                        total = usage.getTotal();
                        // 文件系统资源的利用率
                        pct = (long) (usage.getUsePercent() * 100);
                        // 累计读写总数
                        diskReadBytes = usage.getDiskReadBytes();
                        diskWriteBytes = usage.getDiskWriteBytes();
                    } catch (SigarException e) {
                        // e.g. on win32 D:\ fails with "Device not ready"
                        // if there is no cd in the drive.
                        used = avail = total = pct = diskReadBytes = diskWriteBytes = 0;
                    }

                    // 使用比率
                    String usePct;
                    if (pct == 0) {
                        usePct = "-";
                    } else {
                        usePct = pct + "%";
                    }
                    // 0: TYPE_UNKNOWN ：未知; 1: TYPE_NONE; 2: TYPE_LOCAL_DISK : 本地硬盘
                    // 3: TYPE_NETWORK ：网络; 4: TYPE_RAM_DISK ：闪存;5: TYPE_CDROM ：光驱; 6: TYPE_SWAP ：页面交换
                    // int type = fileSystem.getType();

                    // 分区的盘符名称
                    diskStatus.setName(fileSystem.getDevName());
                    // 使用信息
                    diskStatus.setSize(Sigar.formatSize(total * 1024));
                    diskStatus.setUsed(Sigar.formatSize(used * 1024));
                    diskStatus.setAvail(Sigar.formatSize(avail * 1024));
                    diskStatus.setUseRate(usePct);
                    // 读写
                    diskStatus.setDiskReadBytes(Sigar.formatSize(diskReadBytes));
                    diskStatus.setDiskWriteBytes(Sigar.formatSize(diskWriteBytes));

                    // 挂载
                    diskStatus.setMounte(fileSystem.getDirName());
                    // 类型
                    // 文件系统类型，比如 FAT32、NTFS / 文件系统类型名，比如本地硬盘、光驱、网络文件系统等
                    diskStatus.setType(fileSystem.getSysTypeName() + "/"
                            + fileSystem.getTypeName());

                }
            }

        } catch (Exception e) {
            logger.error("获取磁盘状态失败: ", e);
        } finally {
            // 需要关闭会话，不然长时间创建会话，会导致jvm宕机退出
            sigar.close();
        }
        return diskStatusList;
    }

    /**
     * 获取服务器的网卡状态
     */
    public List<NetInfoStatus> getNetInfoStatusList() {

        List<NetInfoStatus> netInfoStatusList = Lists.newArrayList();
        Sigar sigar = new Sigar();
        try {
            String[] netInfoList = sigar.getNetInterfaceList();
            if (ArrayUtils.isNotEmpty(netInfoList)) {
                for (String netInfo : netInfoList) {
                    // 获取信息
                    NetInterfaceConfig config = sigar
                            .getNetInterfaceConfig(netInfo);

                    // 过滤逻辑网卡  // 过滤本地网卡
                    if (!"127.0.0.1".equalsIgnoreCase(config.getAddress()) && !"0.0.0.0".equalsIgnoreCase(config.getAddress())) {

                        NetInfoStatus netInfoStatus = new NetInfoStatus();
                        // mac
                        String mac = config.getHwaddr().replace(":", "-");
                        netInfoStatus.setMac(mac);
                        String desc = config.getDescription();
                        if (desc.contains(" - ")) {
                            desc = desc.substring(0, desc.indexOf(" - "));
                        }
                        // 描述
                        netInfoStatus.setDescription(desc);
                        // 名称
                        netInfoStatus.setName(config.getName());

                        String hostIp = config.getAddress();

                        // 只加入IPV4的地址
                        if (IPUtils.isIpAddress(hostIp)) {
                            netInfoStatus.setIp(hostIp);
                            // 掩码
                            netInfoStatus.setNetmask(config.getNetmask());
                            // 类型
                            netInfoStatus.setType(config.getType());
                            netInfoStatus.setBroadcast(config.getBroadcast());
                            // 活跃点
                            netInfoStatus.setMetric(config.getMetric() + "");

                            netInfoStatusList.add(netInfoStatus);
                        }

                        try {
                            NetInterfaceStat ifstat = sigar.getNetInterfaceStat(netInfo);
                            // 接收的总包裹数
                            netInfoStatus.setRxPackets(ifstat.getRxPackets() + "");
                            // 发送的总包裹数
                            netInfoStatus.setTxPackets(ifstat.getTxPackets() + "");
                            // 接收到的总字节数
                            netInfoStatus.setRxBytes(ifstat.getRxBytes() + "");
                            // 发送的总字节数
                            netInfoStatus.setTxBytes(ifstat.getTxBytes() + "");
                            // 接收到的错误包数
                            netInfoStatus.setRxErrors(ifstat.getRxErrors() + "");
                            // 发送数据包时的错误数
                            netInfoStatus.setTxErrors(ifstat.getTxErrors() + "");
                            // 接收时丢弃的包数
                            netInfoStatus.setRxDropped(ifstat.getRxDropped() + "");
                            // 发送时丢弃的包数
                            netInfoStatus.setTxDropped(ifstat.getTxDropped() + "");
                        } catch (SigarException ignored) {
                        }
                    }
                }
            }
        } catch (Exception e) {
            logger.error("获取网卡状态失败: ", e);
        } finally {
            // 需要关闭会话，不然长时间创建会话，会导致jvm宕机退出
            sigar.close();
        }
        return netInfoStatusList;
    }

    /**
     * 获取服务器的进程状态
     */
    public List<ProcStatus> getProcStatusList() {

        List<ProcStatus> procStatusList = Lists.newArrayList();
        Sigar sigar = new Sigar();
        try {
            long[] pids = sigar.getProcList();
            if (ArrayUtils.isNotEmpty(pids)) {
                for (long pid : pids) {
                    ProcStatus procStatus = new ProcStatus();
                    procStatusList.add(procStatus);

                    ProcState state = sigar.getProcState(pid);
                    ProcTime time = null;
                    String unknown = "-";

                    // PID
                    procStatus.setPid(String.valueOf(pid));

                    // 用户
                    try {
                        ProcCredName cred = sigar.getProcCredName(pid);
                        procStatus.setUser(cred.getUser());
                    } catch (SigarException e) {
                        procStatus.setUser(unknown);
                    }

                    // 开始时间
                    try {
                        time = sigar.getProcTime(pid);
                        procStatus.setStartTime(getStartTime(time
                                .getStartTime()));
                    } catch (SigarException e) {
                        procStatus.setStartTime(unknown);
                    }

                    // 内存状态
                    try {
                        ProcMem mem = sigar.getProcMem(pid);
                        procStatus.setSize(Sigar.formatSize(mem.getSize()));
                        procStatus.setRss(Sigar.formatSize(mem.getResident()));
                        procStatus.setShare(Sigar.formatSize(mem.getShare()));
                    } catch (SigarException e) {
                        procStatus.setSize(unknown);
                        procStatus.setRss(unknown);
                        procStatus.setShare(unknown);
                    }

                    // 进程状态
                    procStatus.setState(String.valueOf(state.getState()));

                    // CPU时间
                    if (time != null) {
                        long t = time.getTotal() / 1000;
                        procStatus.setCpuTime(t / 60 + ":" + t % 60);
                    } else {
                        procStatus.setCpuTime(unknown);
                    }

                    // 名称
                    String name = ProcUtil.getDescription(sigar, pid);
                    procStatus.setName(name);
                }
            }

        } catch (Exception e) {
            logger.error("获取进程状态失败: ", e);
        } finally {
            // 需要关闭会话，不然长时间创建会话，会导致jvm宕机退出
            sigar.close();
        }
        return procStatusList;
    }

    // ================================================================
    // Getter & Setter
    // ================================================================

    // ================================================================
    // Private Methods
    // ================================================================

    /**
     * 格式化时间
     */
    private static String getStartTime(long time) {
        if (time == 0) {
            return "00:00";
        }
        long timeNow = System.currentTimeMillis();
        String fmt = "MMMd";

        if ((timeNow - time) < ((60 * 60 * 24) * 1000)) {
            fmt = "HH:mm";
        }

        return new SimpleDateFormat(fmt).format(new Date(time));
    }

    // ================================================================
    // Inner or Anonymous Class
    // ================================================================

    // ================================================================
    // Test Methods
    // ================================================================

}
